package com.ryan.mdp.json;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;

import org.springframework.remoting.support.RemoteInvocationUtils;

public class MdpInvocationResult implements Serializable {
    private static final long serialVersionUID = 1L;

    private Object value;

    private Class<?> valueType;

    private Throwable exception;

    private Class<?> exceptionType;

    public MdpInvocationResult() {
    }

    /**
     * Create a new RemoteInvocationResult for the given result value.
     * 
     * @param value
     *            the result value returned by a successful invocation of the
     *            target method
     */
    public MdpInvocationResult(Object value) {
        this.value = value;
        if (value != null) {
            valueType = value.getClass();
        }
    }

    /**
     * Create a new RemoteInvocationResult for the given exception.
     * 
     * @param exception
     *            the exception thrown by an unsuccessful invocation of the
     *            target method
     */
    public MdpInvocationResult(Throwable exception) {
        if (exception != null && (exception instanceof InvocationTargetException)) {
            this.exception = ((InvocationTargetException) exception).getTargetException();
            this.exceptionType = exception.getClass();
        } else {
            this.exception = exception;
            if (exception != null) {
                this.exceptionType = this.exception.getClass();
            }
        }
    }

    public MdpInvocationResult(Object value, Throwable exception) {
        this.value = value;
        if (value != null) {
            valueType = value.getClass();
        }
        if (exception != null && (exception instanceof InvocationTargetException)) {
            this.exception = ((InvocationTargetException) exception).getTargetException();
            this.exceptionType = this.exception.getClass();
        } else {
            this.exception = exception;
            if (exception != null) {
                this.exceptionType = this.exception.getClass();
            }
        }
    }

    /**
     * Return the result value returned by a successful invocation of the target
     * method, if any.
     * 
     * @see #hasException
     */
    public Object getValue() {
        return this.value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public Class<?> getValueType() {
        return valueType;
    }

    public void setValueType(Class<?> valueType) {
        this.valueType = valueType;
    }

    /**
     * Return the exception thrown by an unsuccessful invocation of the target
     * method, if any.
     * 
     * @see #hasException
     */
    public Throwable getException() {
        return this.exception;
    }

    /**
     * Return whether this invocation result holds an exception. If this returns
     * <code>false</code>, the result value applies (even if <code>null</code>).
     * 
     * @see #getValue
     * @see #getException
     */
    public boolean hasException() {
        return (this.exception != null);
    }

    public Class<?> getExceptionType() {
        return exceptionType;
    }

    public void setExceptionType(Class<?> exceptionType) {
        this.exceptionType = exceptionType;
    }

    public void setException(Throwable exception) {
        this.exception = exception;
    }

    /**
     * Return whether this invocation result holds an InvocationTargetException,
     * thrown by an invocation of the target method itself.
     * 
     * @see #hasException()
     */
    public boolean hasInvocationTargetException() {
        return (this.exception != null);
    }

    /**
     * Recreate the invocation result, either returning the result value in case
     * of a successful invocation of the target method, or rethrowing the
     * exception thrown by the target method.
     * 
     * @return the result value, if any
     * @throws Throwable
     *             the exception, if any
     */
    public Object recreate() throws Throwable {
        if (this.exception != null) {
            Throwable exToThrow = this.exception;
            RemoteInvocationUtils.fillInClientStackTraceIfPossible(exToThrow);
            throw exToThrow;
        } else {
            return this.value;
        }
    }

}